#include "XConfigClient.h"
#include <xplatform/thread_pool/XThreadPool.h>
#include <xplatform/XTools.h>
#include <map>
#include <xplatform/protobuf/xmsg_com.pb.h>
#include <sstream>
#include <thread>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/dynamic_message.h>
using namespace google;
using namespace protobuf;
// using namespace compiler;
using namespace std;
#define PB_ROOT "root/"
static std::map<std::string, xmsg::XConfig> conf_map;
static std::mutex config_mutex;
static xmsg::XConfigList *all_config_list_ = nullptr;
static std::mutex all_config_list_mutex_;

class MyMultiFileErrorCollector : public compiler::MultiFileErrorCollector
{
public:
    inline MyMultiFileErrorCollector() {}
    virtual ~MyMultiFileErrorCollector()
    {
    }
    // Line and column numbers are zero-based.  A line number of -1 indicates
    // an error with the entire file (e.g. "not found").
    virtual void AddError(const std::string &filename, int line, int column,
                          const std::string &message)
    {
        XCOM_LOG_ERROR(filename << ":" << line << " column:" << column << " message:" << message);
    }
};

bool XConfigClient::StartGetConfig(const char *server_ip, int server_port,
                                   const char *local_ip, int local_port,
                                   google::protobuf::Message *conf_mes, int timeout_sec)
{
    XConfigClient::RegMsgCallback();
    setServerIP(server_ip);
    setServerPort(server_port);
    if (local_ip)
    {
        strncpy(local_ip_, local_ip, std::min(sizeof(local_ip_), strlen(local_ip)));
    }
    else
    {
        strcpy(local_ip_, "127.0.0.1");
    }
    SetCurrentServiceConf(conf_mes);
    StartConnect();
    if (!waitConnected(10))
    {
        cout << "connect config server failed" << endl;
        return false;
    }
    setTimer(3000);
    return true;
}

bool XConfigClient::GetBool(const char *key, bool *ok)
{
    std::unique_lock<std::mutex> lock(current_service_conf_mutex_);
    if (!current_service_conf_)
    {
        if (ok)
            *ok = false;
        return false;
    }
    // 获取字段
    auto field = current_service_conf_->GetDescriptor()->FindFieldByName(key);
    if (!field)
    {
        if (ok)
            *ok = false;
        return false;
    }
    if (ok)
        *ok = true;
    return current_service_conf_->GetReflection()->GetBool(*current_service_conf_, field);
}

int XConfigClient::GetInt(const char *key, bool *ok)
{
    std::unique_lock<std::mutex> lock(current_service_conf_mutex_);
    if (!current_service_conf_)
    {
        if (ok)
            *ok = false;
        return 0;
    }
    // 获取字段
    auto field = current_service_conf_->GetDescriptor()->FindFieldByName(key);
    if (!field)
    {
        if (ok)
            *ok = false;
        return 0;
    }
    if (ok)
        *ok = true;
    return current_service_conf_->GetReflection()->GetInt32(*current_service_conf_, field);
}

std::string XConfigClient::GetString(const char *key)
{
    std::unique_lock<std::mutex> lock(current_service_conf_mutex_);
    if (!current_service_conf_)
    {
        return "";
    }
    // 获取字段
    auto field = current_service_conf_->GetDescriptor()->FindFieldByName(key);
    if (!field)
    {
        return "";
    }
    return current_service_conf_->GetReflection()->GetString(*current_service_conf_, field);
}

void XConfigClient::timerCB()
{
    if (isConnected() && local_port_ > 0)
        LoadConfig(local_ip_, local_port_);
}

bool XConfigClient::GetConfig(const char *ip, int port, xmsg::XConfig *out_config)
{
    // 构造IP和端口的字符串表示，用于在配置映射中查找
    stringstream ss;
    ss << ip << ":" << port;
    // 记录查找的配置信息的日志
    XCOM_LOG_DEBUG(ss.str().c_str());
    // 加锁以保护配置映射，确保线程安全
    std::unique_lock<std::mutex> lock(config_mutex);
    // 在配置映射中查找配置信息
    if (conf_map.find(ss.str()) != conf_map.end())
    {
        // 如果输出参数不为空，将找到的配置信息复制到输出参数中
        if (out_config)
        {
            out_config->CopyFrom(conf_map[ss.str()]);
        }
        else
        {
            // 如果输出参数为空，记录日志信息
            XCOM_LOG_DEBUG("XConfigClient::GetConfig out_config is null");
        }
        // 找到配置信息，返回true
        return true;
    }
    // 如果未找到配置信息，记录日志信息并返回false
    XCOM_LOG_DEBUG("XConfigClient::GetConfig not found");
    return false;
}

void XConfigClient::Wait()
{
    XThreadPool::Wait();
}

xmsg::XConfigList XConfigClient::GetAllConfig(int page, int page_count, int timeout_sec)
{
    {
        unique_lock<mutex> lock(all_config_list_mutex_);
        if (all_config_list_)
        {
            all_config_list_->Clear();
        }
    }
    // 1 断开连接自动重连
    if (!autoConnect(timeout_sec))
    {
        return xmsg::XConfigList();
    }
    // 2 发送加载配置的请求
    xmsg::XLoadAllConfigReq req;
    req.set_page(page);
    req.set_page_count(page_count);
    SendMsg(xmsg::MSG_LOAD_ALL_CONFIG_REQ, &req);
    int count = timeout_sec * (1000 / 10);
    for (int i = 0; i < count; i++)
    {
        {
            unique_lock<mutex> lock(all_config_list_mutex_);
            if (all_config_list_ && all_config_list_->config_size())
            {
                return *all_config_list_;
            }
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    return xmsg::XConfigList();
}
// 向配置服务器发送配置信息
/**
 * 此函数的目的是将配置信息封装到消息中并发送出去
 * @param head 指向配置信息的指针，该配置信息将被发送
 */
void XConfigClient::SaveConfig(xmsg::XConfig *head)
{
    XCOM_LOG_DEBUG("XConfigClient::SaveConfig");
    SendMsg(xmsg::MSG_SAVE_CONFIG_REQ, head);
}

void XConfigClient::DeleteConfig(const char *ip, int port)
{
    if (!ip || !strlen(ip) || port < 0 || port > 65535)
    {
        XCOM_LOG_ERROR("XConfigClient::DeleteConfig ip or port error");
        return;
    }
    xmsg::XLoadConfigReq req;
    req.set_service_ip(ip);
    req.set_service_port(port);
    SendMsg(xmsg::MSG_DEL_CONFIG_REQ, &req);
}

void XConfigClient::DeleteConfigResCB(xmsg::XMsgHead *head, XMsg *msg)
{
    XCOM_LOG_DEBUG("XConfigClient::DeleteConfigResCB");
    xmsg::XMessageRes res;
    if (res.ParseFromString(msg->data))
    {
        if (res.return_() == xmsg::XMessageRes::SUCCESS)
        {
            XCOM_LOG_DEBUG("XConfigClient::DeleteConfigResCB success");
        }
        else
        {
            XCOM_LOG_ERROR("XConfigClient::DeleteConfigResCB error");
        }
    }
}

google::protobuf::Message *XConfigClient::LoadPoto(const std::string &poto_path, std::string class_name, std::string &out_proto_code)
{
    if (importer_)
        delete importer_;
    importer_ = new compiler::Importer(source_tree_, new MyMultiFileErrorCollector());
    if (!importer_)
        return nullptr;
    // 1 加载Poto文件
    string path = PB_ROOT;
    path += poto_path;
    auto file_desc = importer_->Import(path);
    if (!file_desc)
    {
        return nullptr;
    }
    XCOM_LOG_DEBUG(file_desc);
    const Descriptor *message_desc = nullptr;
    if (class_name.empty())
    {
        if (file_desc->message_type_count() <= 0)
        {
            XCOM_LOG_DEBUG("no message type");
            return nullptr;
        }
        message_desc = file_desc->message_type(0);
    }
    else
    {
        // 包含命名空间的类名 xmsg.XDirConfig
        string class_name_pack = "";
        // 查找类型 命名空间，是否要用户提供

        // 用户没有提供命名空间
        if (class_name.find('.') == class_name.npos)
        {
            if (file_desc->package().empty())
            {
                class_name_pack = class_name;
            }
            else
            {
                class_name_pack = file_desc->package();
                class_name_pack += ".";
                class_name_pack += class_name;
            }
        }
        else
        {
            class_name_pack = class_name;
        }
        message_desc = importer_->pool()->FindMessageTypeByName(class_name_pack);

        if (!message_desc)
        {
            string log = "proto文件中没有指定的message ";
            log += class_name_pack;
            XCOM_LOG_DEBUG(log.c_str());
            return NULL;
        }
    }
    // 反射生成message对象

    // 动态创建消息类型的工厂，不能销毁，销毁后由此创建的message也失效
    static DynamicMessageFactory factory;

    // 创建一个类型原型
    auto message_proto = factory.GetPrototype(message_desc);
    if(message_){
        delete message_;
        message_=nullptr;
    }
    message_ = message_proto->New();
    XCOM_LOG_DEBUG(message_->DebugString());
    /*
    syntax="proto3";	//版本号
    package xmsg;		//命名空间
    message XDirConfig
    {
        string root = 1;
    }
    */

    // auto enum_type = message_desc->enum_type(0);
    // cout << enum_type->DebugString();
    // syntax="proto3";	//版本号
    out_proto_code = "syntax=\"";
    out_proto_code += file_desc->SyntaxName(file_desc->syntax());
    out_proto_code += "\";\n";
    // package xmsg;		//命名空间
    if (!file_desc->package().empty())
    {
        out_proto_code += "package ";
        out_proto_code += file_desc->package();
        out_proto_code += ";\n";
    }

    map<string, const EnumDescriptor *> enum_descs;
    // 添加依赖的枚举
    for (int i = 0; i < message_desc->field_count(); i++)
    {
        auto field = message_desc->field(i);
        if (field->type() == FieldDescriptor::TYPE_ENUM)
        {
            if (enum_descs.find(field->enum_type()->name()) != enum_descs.end())
                continue;
            enum_descs[field->enum_type()->name()] = field->enum_type();
            out_proto_code += field->enum_type()->DebugString();
            out_proto_code += "\n";
        }
    }

    // message XDirConfig
    out_proto_code += message_desc->DebugString();
    return message_;
}

void XConfigClient::SaveConfigResCB(xmsg::XMsgHead *head, XMsg *msg)
{
    if (!SaveConfigResCBFunc)
        return;
    xmsg::XMessageRes res;
    res.ParsePartialFromArray(msg->data, msg->size);
    SaveConfigResCBFunc(xmsg::XMessageRes::SUCCESS == res.return_(), res.msg());
    XCOM_LOG_DEBUG("XConfigClient::SaveConfigResCB");
}

/**
 * @brief 加载配置信息
 *
 * 该函数用于向服务端发送加载配置的请求。它将指定的IP和端口信息
 * 封装到一个xmsg::XConfig对象中，并通过SendMsg函数发送出去。
 *
 * @param ip 微服务的IP地址
 * @param port 微服务的IP地址
 */
void XConfigClient::LoadConfig(const char *ip, int port)
{
    if (port < 0 || port > 65535)
        return;
    // 创建一个XConfig对象，用于封装配置信息
    xmsg::XLoadConfigReq config;
    if (ip)
    {
        // 微服务的IP地址
        config.set_service_ip(ip);
    }
    // 微服务的port
    config.set_service_port(port);

    // 发送加载配置的请求消息
    SendMsg(xmsg::MSG_LOAD_CONFIG_REQ, &config);
}

// 处理加载配置资源的回调函数
void XConfigClient::LoadConfigResCB(xmsg::XMsgHead *head, XMsg *msg)
{
    xmsg::XConfig config;
    // 解析配置信息，如果解析失败，则记录日志并返回
    if (!config.ParseFromArray(msg->data, msg->size))
    {
        XCOM_LOG_DEBUG("ParseFromArray failed");
        return;
    }
    // 解析成功后，记录配置信息中的IP、端口和名称
    XCOM_LOG_DEBUG("ip:" << config.service_ip() << " port:" << config.service_port() << " name:" << config.service_name());
    stringstream key;
    key << config.service_ip() << ":" << config.service_port();
    // 加锁以保护配置映射，确保线程安全
    config_mutex.lock();
    conf_map[key.str()] = config;
    config_mutex.unlock();
    // 如果本地端口已设置且当前服务配置存在，则检查是否需要更新当前服务配置
    std::unique_lock<std::mutex> lock(current_service_conf_mutex_);
    if (local_port_ > 0 && current_service_conf_)
    {
        stringstream local_key;
        local_key << config.service_ip() << ":" << local_port_;
        // 如果当前配置与本地服务匹配，则复制配置信息
        if (local_key.str() == key.str())
        {
            XCOM_LOG_DEBUG("&");
            current_service_conf_->CopyFrom(config);
        }
    }
}

void XConfigClient::LoadAllConfigResCB(xmsg::XMsgHead *head, XMsg *msg)
{
    unique_lock<mutex> lock(all_config_list_mutex_);
    if (!all_config_list_)
    {
        all_config_list_ = new xmsg::XConfigList();
    }
    all_config_list_->ParseFromArray(msg->data, msg->size);
}

XConfigClient::~XConfigClient()
{
}

XConfigClient::XConfigClient()
{
    setDeleteOnClose(false);
    source_tree_ = new compiler::DiskSourceTree();
    source_tree_->MapPath("", "");
    source_tree_->MapPath(PB_ROOT, "");
}
